// interfaces/music5/Music5.java
// (c)2021 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// {java interfaces.music5.Music5}
package com.lmq.interfaces.music5;
import com.lmq.polymorphism.music.Note;

interface Instrument {
  // Compile-time constant:
  int VALUE = 5; // static & final
  default void play(Note n) {  // Automatically public
    System.out.println(this + ".play() " + n);
  }
  default void adjust() {
    System.out.println("Adjusting " + this);
  }
}

class Wind implements Instrument {
  @Override public String toString() {
    return "Wind";
  }
}

class Percussion implements Instrument {
  @Override public String toString() {
    return "Percussion";
  }
}

class Stringed implements Instrument {
  @Override public String toString() {
    return "Stringed";
  }
}

class Brass extends Wind {
  @Override public String toString() {
    return "Brass";
  }
}

class Woodwind extends Wind {
  @Override public String toString() {
    return "Woodwind";
  }
}

public class Music5 {
  // Doesn't care about type, so new types
  // added to the system still work right:
  static void tune(Instrument i) {
    // ...
    i.play(Note.MIDDLE_C);
  }
  static void tuneAll(Instrument[] e) {
    for(Instrument i : e)
      tune(i);
  }
  public static void main(String[] args) {
    // Upcasting during addition to the array:
    Instrument[] orchestra = {
      new Wind(),
      new Percussion(),
      new Stringed(),
      new Brass(),
      new Woodwind()
    };
    tuneAll(orchestra);
  }
}
/* Output:
Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C
*/
/**
 * Especially with the addition of default methods in java 8, it
 * become somewhat confusing to know when an abstract class isthe best choice ,
 * and when you should instead use an interface. This table should make the distinction clear:
 * An abstract class is still a class, and can thus be the only class inherited
 * when creating a new class.Multiple interfaces can be implemented in
 * the process of creating a new class.
 * A rule of thumb is to "be as abstract as possible --- within reason".
 * Thus, prefer interfaces over abstract classes.
 * You'll know when you must use an abstract class.
 * And don't use either one unless you must.
 * Most the time,
 * a regular class will do the trick,
 * and when it doesn't, you can move to an interface or abstract class.
 *
 */
/**
 * Complete Decoupling
 * Whenever a method works with a class instead of an interface,
 * you are limited to using that class or its subclass.If
 * you would like to apply the method to a class that isn't in that hierarchy,
 * you're out of luck.An interface relaxes this constraint considerably.
 * As a result, you can write more reusable code.
 * For example, suppose you have a Processor class with name() and process() methods
 * process() takes input, modifies it and produces output.This is
 * inherited to create different types of Processor.Here, the
 * Processor types modify String objects(note that the return types
 * can be covariant, but not the argument types):
 */